package server

import (
	"encoding/json"
	"fmt"
	"io"
	"sync"

	"github.com/YaxiongWu/remote-control-webrtc/pkg/stun"
	pb "github.com/YaxiongWu/remote-control-webrtc/signal/grpc/proto"
	"github.com/pion/webrtc/v3"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

type STUNServer struct {
	pb.UnimplementedSFUServer
	sync.Mutex
	STUN *stun.STUN
}

func NewServer(stun *stun.STUN) *STUNServer {
	return &STUNServer{STUN: stun}
}

// Publish a stream to the sfu. Publish creates a bidirectional
// streaming rpc connection between the client and sfu.
//
// The sfu will respond with a message containing the stream pid
// and one of two different payload types:
// 1. `Connect` containing the session answer description. This
// message is *always* returned first.
// 2. `Trickle` containing candidate information for Trickle ICE.
//
// If the webrtc connection is closed, the server will close this stream.
//
// The client should send a message containing the session id
// and one of two different payload types:
// 1. `Connect` containing the session offer description. This
// message must *always* be sent first.
// 2. `Trickle` containing candidate information for Trickle ICE.
//
// If the client closes this stream, the webrtc stream will be closed.
func (s *STUNServer) Signal(stream pb.SFU_SignalServer) error {
	//peer := sfu.NewPeer(s.SFU)
	//return error.New("wuyaxiong ,server.go Signal error test")
	client := stun.NewClient(s.STUN)
	for {
		in, err := stream.Recv()

		if err != nil {
			//peer.Close()

			if err == io.EOF {
				return nil
			}

			errStatus, _ := status.FromError(err)
			if errStatus.Code() == codes.Canceled {
				return nil
			}

			//sfu.Logger.Error(fmt.Errorf(errStatus.Message()), "signal error", "code", errStatus.Code())
			return err
		}

		//sfu.Logger.V(1).Info("in.Payload:", in.Payload)
		//fmt.Printf("payload:%v\r\n", in.Payload)

		switch payload := in.Payload.(type) {
		case *pb.SignalRequest_RegisterNewSource:
			//stun.LogDebug.Printf("SignalRequest_RegisterNewSource:%v\r\n", (payload.RegisterNewSource))
			err = stream.Send(&pb.SignalReply{
				Payload: &pb.SignalReply_RegisterNewSource{
					RegisterNewSource: "Register succeed",
				},
			})
			//client.Id = payload.RegisterNewSource.Uid
			//client.SetID(payload.RegisterNewSource.Uid)
			stun.LogDebug.Print("payload.RegisterNewSource: ", payload.RegisterNewSource)
			client.CreateSession(payload.RegisterNewSource.Sid)
			client.Join(payload.RegisterNewSource.Sid, payload.RegisterNewSource.Uid)
			client.OnOfferJSON2GRCP = func(o *webrtc.SessionDescription) {
				marshalled, err := json.Marshal(o)
				//stun.LogDebug.Printf("grcp recv,offer:%s\r\n", marshalled)
				if err != nil {
					s.Lock()
					err = stream.Send(&pb.SignalReply{
						Payload: &pb.SignalReply_Error{
							Error: fmt.Errorf("offer sdp marshal error: %w", err).Error(),
						},
					})
					s.Unlock()
					if err != nil {
						stun.LogDebug.Print(err, "grpc send error")
					}
					return
				}

				s.Lock()
				err = stream.Send(&pb.SignalReply{
					Payload: &pb.SignalReply_Description{
						Description: marshalled,
					},
				})
				s.Unlock()

				if err != nil {
					stun.LogDebug.Print(err, "negotiation error")
				}
			}

			client.OnIceCandidateJSON2GRCP = func(candidate *webrtc.ICECandidateInit, target int) {
				bytes, err := json.Marshal(candidate)
				if err != nil {
					stun.LogDebug.Print(err, "OnIceCandidate error")
				}
				s.Lock()
				err = stream.Send(&pb.SignalReply{
					Payload: &pb.SignalReply_Trickle{
						Trickle: &pb.Trickle{
							Init:   string(bytes),
							Target: pb.Trickle_Target(target),
						},
					},
				})
				s.Unlock()
				if err != nil {
					stun.LogDebug.Print(err, "OnIceCandidate send error")
				}
			}

			client.OnICEConnStateChangeJSON2GRCP = func(c webrtc.ICEConnectionState) {
				s.Lock()
				err = stream.Send(&pb.SignalReply{
					Payload: &pb.SignalReply_IceConnectionState{
						IceConnectionState: c.String(),
					},
				})
				s.Unlock()

				if err != nil {
					stun.LogDebug.Print(err, "oniceconnectionstatechange error")
				}
			}
			break
		case *pb.SignalRequest_Join:
			//sfu.Logger.V(1).Info("signal->join", "called", string(payload.Join.Description))
			var offer webrtc.SessionDescription
			err = json.Unmarshal(payload.Join.Description, &offer)
			if err != nil {
				s.Lock()
				err = stream.Send(&pb.SignalReply{
					Payload: &pb.SignalReply_Error{
						Error: fmt.Errorf("join sdp unmarshal error: %w", err).Error(),
					},
				})
				s.Unlock()
				if err != nil {
					stun.LogDebug.Print(err, "grpc send error")
					return status.Errorf(codes.Internal, err.Error())
				}
			}

			break
		case *pb.SignalRequest_Description:

			var sdp webrtc.SessionDescription
			err = json.Unmarshal(payload.Description, &sdp)
			stun.LogDebug.Printf("sdp type:%v\r\n", sdp.Type)
			stun.LogDebug.Printf("grpc recv sdp:%v\r\n", sdp)
			if err != nil {
				s.Lock()
				stun.LogError.Print(err, "grpc send error")
				err = stream.Send(&pb.SignalReply{
					Payload: &pb.SignalReply_Error{
						Error: fmt.Errorf("negotiate sdp unmarshal error: %w", err).Error(),
					},
				})
				s.Unlock()
				if err != nil {
					return status.Errorf(codes.Internal, err.Error())
				}
			}

			clients := client.Session().Clients()
			for c := range clients {
				if clients[c].GetID() != client.GetID() {
					//join时，是从网页发起的，会等待树莓派的回复，用Reply，视频是从树莓派发起的，需要用Notify
					if sdp.Type == webrtc.SDPTypeAnswer {
						if clients[c].OnOfferGRCP2JSONReply != nil {
							clients[c].OnOfferGRCP2JSONReply(&sdp)
						}
					} else if sdp.Type == webrtc.SDPTypeOffer {
						if clients[c].OnOfferGRCP2JSONNotify != nil {
							clients[c].OnOfferGRCP2JSONNotify(&sdp)
						}
					}
				}
			}
			break
		case *pb.SignalRequest_Trickle:
			var candidate webrtc.ICECandidateInit
			err := json.Unmarshal([]byte(payload.Trickle.Init), &candidate)
			stun.LogDebug.Print("grpc recv,candidate:", candidate)
			if err != nil {
				stun.LogDebug.Print(err, "error parsing ice candidate")
				s.Lock()
				err = stream.Send(&pb.SignalReply{
					Payload: &pb.SignalReply_Error{
						Error: fmt.Errorf("unmarshal ice candidate error:  %w", err).Error(),
					},
				})
				s.Unlock()
				if err != nil {
					stun.LogDebug.Print(err, "grpc send error ")
					return status.Errorf(codes.Internal, err.Error())
				}
				continue
			}
			clients := client.Session().Clients()
			for c := range clients {
				if clients[c].GetID() != client.GetID() {
					if clients[c].OnIceCandidateGRCP2JSON != nil {
						//pub和sub问题是相对的，具体问题还有待进一步优化
						tempTarget := 0
						if int(payload.Trickle.Target) == 0 {
							tempTarget = 1
						}
						clients[c].OnIceCandidateGRCP2JSON(&candidate, tempTarget) //int(payload.Trickle.Target))
					}
				}
			}
			break
		default:
			break
		}

		//}

		//}
	}
}

/*
{"type":"offer","sdp":"v=0\r\no=- 5603762590042449336 1642124569 IN IP4 0.0.0.0\r\ns=-\r\nt=0 0\r\na=fingerprint:sha-256 8D:A8:41:36:36:5B:82:11:FE:8D:A6:92:CC:CC:DE:6F:A3:D3:EE:D5:65:2E:F4:D2:30:19:96:74:A1:2A:CE:0C\r\na=group:BUNDLE 0 1\r\nm=video 9 UDP/TLS/RTP/SAVPF 96 98 100 102 127 125 108 123\r\nc=IN IP4 0.0.0.0\r\na=setup:actpass\r\na=mid:0\r\na=ice-ufrag:zkDNTXwPSRvzzbdT\r\na=ice-pwd:rENrrhdUoZdXyvHKddNqfjEiLIGIbWHw\r\na=rtcp-mux\r\na=rtcp-rsize\r\na=rtpmap:96 vp8/90000\r\na=rtcp-fb:96 goog-remb \r\na=rtcp-fb:96 ccm fir\r\na=rtcp-fb:96 nack \r\na=rtcp-fb:96 nack pli\r\na=rtpmap:98 vp9/9000
*/
